package com.isesol.ime.service;

import com.alibaba.fastjson.*;
import com.alibaba.fastjson.serializer.*;
import com.github.pagehelper.*;
import com.google.common.collect.*;
import com.isesol.arch.common.model.Result;
import com.isesol.arch.common.mq.*;
import com.isesol.arch.common.service.*;
import com.isesol.arch.common.utils.*;
import com.isesol.arch.fluentvalidator.*;
import com.isesol.ime.Constant;
import com.isesol.ime.dal.mapper.*;
import com.isesol.ime.dal.model.*;
import com.isesol.ime.dal.model.dto.*;
import com.isesol.ime.enums.*;
import com.isesol.ime.service.mq.topic.*;
import com.isesol.ime.util.*;
import com.isesol.ime.validator.subscription.*;
import lombok.extern.slf4j.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;

import javax.annotation.*;
import java.util.*;

/**
 * 订阅管理
 *
 * @author peter zhang
 */
@Component
@Slf4j
public class SubscriptionService extends BaseService {

	@Resource
	SubscriptionMapper subscriptionMapper;

	@Resource
	MessageMapper messageMapper;

	@Resource
	SubscriptionFeedbackMapper subscriptionFeedbackMapper;

	@Resource
	SendOrderService sendOrderService;

	@Autowired(required = false)
	MsgSender msgSender;

	/**
	 * 创建初始订阅
	 *
	 * @param machineId
	 * @param machineType
	 */
	public Result initSubscription(String machineId, String machineType) {

		List<MessageDO> messageDOList = messageMapper.findDefault(machineType);

		log.info("initSubscription,machineType={},messageDOList={}",machineType,messageDOList);

		for (MessageDO messageDO : messageDOList) {

			SubscriptionDO subscriptionDO = subscriptionMapper.findByMachineIdAndMsgId(machineId, messageDO.getId());
			//首次创建机床时
			if (null == subscriptionDO) {
				SubscriptionDO subscription = new SubscriptionDO();
				subscription.setMsgId(messageDO.getId());
				subscription.setMachineType(machineType);
				subscription.setMachineId(machineId);
				subscription.setRuleName(messageDO.getRuleName());
				subscription.setUsingStatus(StatusEnum.ON.getStatus());
				subscription.setStatus(StatusEnum.ON.getStatus());
				subscriptionMapper.insert(subscription);

				SubscriptionFeedbackDO subscriptionFeedbackDO = SubscriptionFeedbackDO.builder()
						.feedback(FeedbackEnum.OFF.getStatus())
						.machineId(machineId)
						.status(StatusEnum.ON.getStatus())
						.subId(subscription.getId())
						.ruleName(messageDO.getRuleName())
						.build();
				subscriptionFeedbackMapper.insert(subscriptionFeedbackDO);
			} else {
				subscriptionDO.setMsgId(messageDO.getId());
				subscriptionDO.setMachineType(machineType);
				subscriptionDO.setMachineId(machineId);
				subscriptionDO.setRuleName(messageDO.getRuleName());
				subscriptionDO.setUsingStatus(StatusEnum.ON.getStatus());
				subscriptionDO.setStatus(StatusEnum.ON.getStatus());
				subscriptionMapper.update(subscriptionDO);

				SubscriptionFeedbackDO subscriptionFeedbackDO = subscriptionFeedbackMapper.findBySubId(subscriptionDO.getId());
				if (null != subscriptionFeedbackDO) {
					subscriptionFeedbackDO.setStatus(StatusEnum.ON.getStatus());
					subscriptionFeedbackDO.setFeedback(FeedbackEnum.OFF.getStatus());
					subscriptionFeedbackMapper.update(subscriptionFeedbackDO);
				} else {
					subscriptionFeedbackDO = SubscriptionFeedbackDO.builder()
							.feedback(FeedbackEnum.OFF.getStatus())
							.machineId(machineId)
							.status(StatusEnum.ON.getStatus())
							.subId(subscriptionDO.getId())
							.ruleName(messageDO.getRuleName())
							.build();
					subscriptionFeedbackMapper.insert(subscriptionFeedbackDO);
				}
			}
		}

		return assembleSuccessResult(null);
	}


	public boolean modify(Map<String, SubscriptionDO> map, String id, List modifyList, String usingStatus) {

		boolean modify = false;

		SubscriptionDO originSubscriptionDO = map.get(id);
		if (originSubscriptionDO != null && !originSubscriptionDO.getUsingStatus().equals(usingStatus)) {

			originSubscriptionDO.setUsingStatus(usingStatus);
			originSubscriptionDO.setStatus(StatusEnum.ON.getStatus());
			modifyList.add(originSubscriptionDO);

			modify = true;
		}

		return modify;
	}

	public void sendFrequencyOrder(long subId) throws Exception {

		SubscriptionDO subscriptionDO = subscriptionMapper.getById(subId);
		if (null != subscriptionDO
				&& subscriptionDO.getUsingStatus().equals(StatusEnum.ON.getStatus())) {
			MessageDO messageDO = messageMapper.getById(subscriptionDO.getMsgId());

			Map<String, String> infoMap = Maps.newHashMap();
			infoMap.put("subId", String.valueOf(subId));
			infoMap.put("rule", messageDO.getRule());
			infoMap.put("ruleName", messageDO.getRuleName());
			infoMap.put("machineId", subscriptionDO.getMachineId());
			infoMap.put("msgNumber", messageDO.getMsgNumber());

			String jsonMsg = JSON.toJSONString(infoMap, SerializerFeature.WriteClassName);
			// send with mqtt
			if (EnvUtil.sendWithMqtt()) {
				sendOrderService.sendOrder(jsonMsg);
			} else {
				msgSender.sendMessage(jsonMsg, SendOrderTopic.class);
			}

		}
	}

	public void sendDownloadOrder(String machineId, String subId) throws Exception {

		Map infoMap = Maps.newHashMap();
		String configFileDownloadUrl = PropertyFileUtil.get(Constant.CONFIG_FILE_DOWNLOAD_URL);

		SubscriptionDO subscriptionDO = subscriptionMapper.getById(Long.valueOf(subId));
		MessageDO messageDO = messageMapper.getById(subscriptionDO.getMsgId());
		String path = "";
		if ("time".equals(messageDO.getRuleName())) {

			subscriptionFeedbackMapper.updateFeedbackStatusByMachineIdAndRuleName(machineId,"time");
			path = "subscrData";
		} else if ("event".equals(messageDO.getRuleName())) {
			subscriptionFeedbackMapper.updateFeedbackStatusByMachineIdAndRuleName(machineId,"event");
			path = "eventData";
		}

		infoMap.put("downloadUrl", configFileDownloadUrl + subId);
		infoMap.put("path", path);
		infoMap.put("id", String.valueOf(subId));

		try {
			sendOrderService.sendOrder(JSON.toJSONString(infoMap),
					Constant.DOWNLOAD_FEEDBACK + "|" + subId,
					"91", 7, "1", "on", machineId);

		} catch (Exception e) {

			log.warn("调用下发命令接口失败",e);

			throw new ServiceException("调用下发命令接口失败", e);
		}

	}


	/**
	 * 订阅新增
	 *
	 * @param subscriptionDTO
	 * @return
	 * @throws Exception
	 */
	public void create(SubscriptionDTO subscriptionDTO) throws Exception {

		FluentValidator.checkAll().failOver()
				.on(subscriptionDTO, SpringContextHolder.getBean(SubscriptionCreateValidator.class))
				.on(subscriptionDTO, SpringContextHolder.getBean(SubscriptionCreateMsgExistValidator.class))
				.doValidate();

		String machineId = subscriptionDTO.getMachineId();
		String[] ids = subscriptionDTO.getMsgIds().split(",");

		MessageDO messageDO = messageMapper.getById(Long.parseLong(ids[0]));
		String ruleName = messageDO.getRuleName();
		Map<String, String> idsMap = Maps.newHashMap();
		for (String id : ids) {
			idsMap.put(id, id);
		}
		Map<String, SubscriptionDO> originAllMap = Maps.newHashMap();
		Map<String, SubscriptionDO> originEnableMap = Maps.newHashMap();
		Map<String, SubscriptionDO> originDisableMap = Maps.newHashMap();

		List<SubscriptionDO> modifyList = Lists.newArrayList();
		List<SubscriptionDO> insertList = Lists.newArrayList();

		List<SubscriptionDO> subscriptionDOList = subscriptionMapper.findByMachineIdAndRuleName(machineId, messageDO.getRuleName());
		for (SubscriptionDO subscriptionDO : subscriptionDOList) {
			String msgId = String.valueOf(subscriptionDO.getMsgId());
			if (subscriptionDO.getStatus().equals(StatusEnum.ON.getStatus())) {
				originEnableMap.put(msgId, subscriptionDO);
			}
			if (subscriptionDO.getStatus().equals(StatusEnum.OFF.getStatus())) {
				originDisableMap.put(msgId, subscriptionDO);
			}
			originAllMap.put(msgId, subscriptionDO);
		}

		for (String key : originAllMap.keySet()) {
			// 原来订阅了，现在不订阅了，置旧的status='0'
			if (!idsMap.containsKey(key)) {
				SubscriptionDO subscriptionDO = originAllMap.get(key);
				if (subscriptionDO.getStatus().equals(StatusEnum.ON.getStatus())) {
					subscriptionDO.setStatus(StatusEnum.OFF.getStatus());
					modifyList.add(subscriptionDO);
				}
			}
		}

		for (int i = 0; i < ids.length; i++) {
			String id = ids[i];

			// 都没有，需要新增一个订阅
			if (!originAllMap.containsKey(id)) {

				SubscriptionDO subscriptionDO = new SubscriptionDO();
				subscriptionDO.setUsingStatus(subscriptionDTO.getUsingStatus());
				subscriptionDO.setMachineType(subscriptionDTO.getMachineType());
				subscriptionDO.setStatus(StatusEnum.ON.getStatus());
				subscriptionDO.setMachineId(subscriptionDTO.getMachineId());
				subscriptionDO.setMsgId(Long.parseLong(id));
				subscriptionDO.setRuleName(messageMapper.findById(id).getRuleName());
				insertList.add(subscriptionDO);

				continue;
			}

			if (originDisableMap.containsKey(id)) {
				//原来不订阅的，现在订阅的 ,置status = 1
				SubscriptionDO subscriptionDO = originAllMap.get(id);
				subscriptionDO.setStatus(StatusEnum.ON.getStatus());
				subscriptionDO.setUsingStatus(subscriptionDTO.getUsingStatus());
				modifyList.add(subscriptionDO);

				continue;
			}

			modify(originEnableMap, id, modifyList, subscriptionDTO.getUsingStatus());
			modify(originDisableMap, id, modifyList, subscriptionDTO.getUsingStatus());

		}

		for (SubscriptionDO subscriptionDO : modifyList) {
			subscriptionDO.setUsingStatus(subscriptionDTO.getUsingStatus());
			subscriptionMapper.update(subscriptionDO);

		}
		for (SubscriptionDO subscriptionDO : insertList) {
			subscriptionDO.setUsingStatus(subscriptionDTO.getUsingStatus());
			subscriptionMapper.insert(subscriptionDO);
		}

		List<Long> feedBackSubIdList = subscriptionMapper.findValidSubIdByMachineIdAndRuleName(machineId, ruleName);

		for (Long subId : feedBackSubIdList) {
			SubscriptionFeedbackDO subscriptionFeedbackDO = subscriptionFeedbackMapper.findBySubId(subId);
			if (null != subscriptionFeedbackDO) {
				subscriptionFeedbackDO.setFeedback(FeedbackEnum.OFF.getStatus());
				subscriptionFeedbackDO.setStatus(StatusEnum.ON.getStatus());
				subscriptionFeedbackMapper.update(subscriptionFeedbackDO);
			} else {
				subscriptionFeedbackDO = new SubscriptionFeedbackDO();
				subscriptionFeedbackDO.setMachineId(machineId);
				subscriptionFeedbackDO.setStatus(StatusEnum.ON.getStatus());
				subscriptionFeedbackDO.setFeedback("0");
				subscriptionFeedbackDO.setSubId(subId);
				subscriptionFeedbackDO.setRuleName(subscriptionMapper.getById(subId).getRuleName());
				subscriptionFeedbackMapper.insert(subscriptionFeedbackDO);
			}
		}
	}

	/**
	 * 启用
	 *
	 * @param id
	 */
	public Result startUsing(long id) throws Exception {

		SubscriptionDTO subscriptionDTO = new SubscriptionDTO();
		subscriptionDTO.setId(id);

		FluentValidator.checkAll().failOver()
				.on(subscriptionDTO, SpringContextHolder.getBean(SubscriptioStartUsingValidator.class))
				.doValidate();

		SubscriptionDO subscriptionDO = subscriptionMapper.getById(id);
		subscriptionDO.setUsingStatus(StatusEnum.ON.getStatus());
		subscriptionMapper.update(subscriptionDO);

		return assembleSuccessResult(null);
	}

	/**
	 * 停用
	 *
	 * @param id
	 */
	public void stopUsing(long id) throws Exception {

		SubscriptionDO subscriptionDO = subscriptionMapper.getById(id);
		if (null != subscriptionDO) {
			subscriptionDO.setUsingStatus(StatusEnum.OFF.getStatus());
			subscriptionMapper.update(subscriptionDO);
			MessageDO messageDO = messageMapper.getById(subscriptionDO.getMsgId());
			SubscriptionFeedbackDO subscriptionFeedbackDO = subscriptionFeedbackMapper.findBySubId(id);
			if (null != subscriptionFeedbackDO) {
				if (!subscriptionFeedbackDO.getStatus().equals(StatusEnum.OFF.getStatus())) {
					subscriptionFeedbackDO.setFeedback(FeedbackEnum.OFF.getStatus());
					subscriptionFeedbackDO.setStatus(StatusEnum.ON.getStatus());
					subscriptionFeedbackMapper.update(subscriptionFeedbackDO);
				}
			} else {
				//insert new subscriptionFeedbackDO
				subscriptionFeedbackDO = SubscriptionFeedbackDO.builder()
						.status(StatusEnum.ON.getStatus())
						.feedback("0")
						.machineId(subscriptionDO.getMachineId())
						.subId(id)
						.ruleName(subscriptionMapper.getById(id).getRuleName())
						.build();
				subscriptionFeedbackMapper.insert(subscriptionFeedbackDO);
			}

			try{
				sendOrderService.sendOrder(
						"{}",
						"frequencyFeedback|" + id,
						messageDO.getMsgNumber(),
						7,
						"0",
						"off",
						subscriptionDO.getMachineId());
			} catch (Exception e) {

			throw new ServiceException("调用下发命令接口失败", e);
		}


		} else {
			throw new ServiceException("订阅id不存在: " + id);
		}

	}

	/**
	 * 删除订阅 ， 逻辑删除
	 *
	 * @param id
	 */
	public void del(long id) {

		SubscriptionDO subscriptionDO = subscriptionMapper.getById(id);
		if (null != subscriptionDO) {
			subscriptionDO.setStatus(StatusEnum.OFF.getStatus());
			subscriptionMapper.update(subscriptionDO);
		}
	}

	/**
	 * 已选报文
	 *
	 * @param machineId
	 * @return
	 */
	public List<MessageDTO> getSelectedMessages(String ruleName, String machineId) {

		List<MessageDTO> messageList = Lists.newArrayList();

		List<SubscriptionDO> subscriptionDOList = subscriptionMapper.findByMachineIdAndStatus(ruleName, machineId, StatusEnum.ON.getStatus());

		for (int i = 0; i < subscriptionDOList.size(); i++) {

			Long msgId = subscriptionDOList.get(i).getMsgId();
			MessageDTO messageDtO = messageMapper.findById(String.valueOf(msgId));
			messageList.add(messageDtO);
		}

		return messageList;
	}

	/**
	 * 查询分页
	 *
	 * @param subscriptionDTO
	 * @param page
	 * @param rows
	 * @return
	 */
	public PageInfo find(SubscriptionDTO subscriptionDTO, int page, int rows) {

		PageHelper.startPage(page, rows);

		List<SubscriptionDTO> subscriptionDetailDTOList = subscriptionMapper.find(subscriptionDTO.getMachineId(),
				subscriptionDTO.getMsgId(),
				subscriptionDTO.getMachineType(),
				subscriptionDTO.getRuleName());

		return new PageInfo(subscriptionDetailDTOList);

	}

	/**
	 * 该机床可选报文
	 */
	public List<MessageDTO> getSelectableMessages(String machineType, String machineId, String ruleName) throws Exception {

		return messageMapper.SelectableMessages(machineId, machineType, ruleName, StatusEnum.ON.getStatus());

	}

	/**
	 * 批量复制订阅
	 *
	 * @param sourceMachineId
	 * @param machineIds
	 * @return
	 */
	public void batchCopy(String sourceMachineId, String machineIds) throws Exception {

		SubscriptionDTO subscriptionDTO = SubscriptionDTO.builder()
				.machineIds(machineIds)
				.machineId(sourceMachineId)
				.build();

		FluentValidator.checkAll().failOver()
				.on(subscriptionDTO, SpringContextHolder.getBean(SubscriptionBatchCopyValidator.class))
				.doValidate();

		String[] machine = machineIds.split(",");
		List<SubscriptionDO> subscriptionList =
				subscriptionMapper.findByMachineIdAndStatus("", sourceMachineId, StatusEnum.ON.getStatus());

		/**
		 *  删除已经订阅的报文
		 */
		for (int i = 0; i < machine.length; i++) {

			List<SubscriptionDO> subscriptionDOList = subscriptionMapper.findByMachineIdAndStatus("", machine[i], StatusEnum.ON.getStatus());

			if (!subscriptionDOList.isEmpty()) {

				for (SubscriptionDO subscr : subscriptionDOList) {

					subscr.setStatus(StatusEnum.OFF.getStatus());
					subscriptionMapper.update(subscr);

				}
			}
		}

		/**
		 * 开始新增订阅
		 */
		for (int i = 0; i < machine.length; i++) {
			for (SubscriptionDO subscription : subscriptionList) {

				SubscriptionDO tempSub = new SubscriptionDO();

				tempSub.setMachineId(machine[i]);
				tempSub.setMsgId(subscription.getMsgId());
				tempSub.setUsingStatus(subscription.getUsingStatus());
				tempSub.setStatus(StatusEnum.ON.getStatus());
				tempSub.setMachineId(subscription.getMachineId());
				tempSub.setMachineType(subscription.getMachineType());
				tempSub.setRuleName(subscription.getRuleName());
				subscriptionMapper.insert(tempSub);

			}
		}
	}

	public void pushAll(String machineType) {

		Map<String, String> infoMap = Maps.newHashMap();
		infoMap.put("machineType", machineType);

		String jsonMsg = JSON.toJSONString(infoMap, SerializerFeature.WriteClassName);

		msgSender.sendMessage(jsonMsg, PushAllTopic.class);

	}

	public List<PushDTO> findAllMachineByMachineType(String machineType) {

		return subscriptionMapper.findMachineIdByMachineType(machineType);
	}

	public List<PushDTO> findOneSubByMachineId(String machineId) {

		return subscriptionMapper.findOneByMachineId(machineId);
	}

	public void setSendOrderService(SendOrderService sendOrderService) {
		this.sendOrderService = sendOrderService;
	}
}
